package com.bikes.mileagetracker.service

import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.location.*
import android.os.Binder
import android.os.Bundle
import android.os.IBinder
import android.os.IInterface
import android.util.Log
import com.bikes.mileagetracker.MileageTrackerApplication
import com.bikes.mileagetracker.R
import com.bikes.mileagetracker.entity.LocationEntity
import com.bikes.mileagetracker.entity.TrackingEntity
import com.bikes.mileagetracker.service.LocationService.Companion.LOCATION_ENTITY_CREATE_DELEGATES
import com.bikes.mileagetracker.utils.*
import org.apache.commons.lang3.StringUtils
import java.text.SimpleDateFormat
import java.util.*

const val LOCATION_MIN_TIME = 5000L
const val LOCATION_MIN_DISTANCE = 5.0f


class LocationServiceImpl : Binder(), IInterface, LocationListener {



    lateinit var locationManager: LocationManager

    var latestLocation: Location? = null
    var distance = 0.0f

    override fun asBinder(): IBinder {
        return this
    }

    fun initTracking() {
        latestLocation = null
        distance = 0.0f


        val formatDate = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
        val formatTime = SimpleDateFormat("hh:mm:ss", Locale.getDefault())

        TrackingEntity().apply {
            this.userId = MileageTrackerApplication.getCurrentAccount()?.id
            val tm = Date()
            this.startTm = tm.time
            this.startDate = formatDate.format(tm)
            this.startTime = formatTime.format(tm)

            MileageTrackerApplication.updateCurrentTracking(this)

            waitPersistenceServiceReaderRun {
                PersistenceService.service!!.saveTracking(this)
                Log.v("console", "new tracking id is ${this.id}")
                LocationService.LOCATION_UPDATE_DELEGATES.remove(this@LocationServiceImpl::onMove)
                LocationService.LOCATION_UPDATE_DELEGATES.add(0,this@LocationServiceImpl::onMove)

            }
        }

    }

    fun stopTracking() {
        if (latestLocation is Location) {
            AddressUtils.recoAddress(latestLocation!!)?.also {
                MileageTrackerApplication.getCurrentTracking()?.apply {
                    endLatitude = latestLocation!!.latitude
                    endLongitude = latestLocation!!.longitude
                    endAddress = it

                    val formatDate = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
                    val formatTime = SimpleDateFormat("hh:mm:ss", Locale.getDefault())

                    val tm = Date()
                    this.endTm = tm.time
                    this.endDate = formatDate.format(tm)
                    this.endTime = formatTime.format(tm)

                    this.miles = Math.ceil(distance.toDouble()).toLong()

                    PersistenceService.service?.saveTracking(this)
                }
            }
        } else {
            Log.v("console", "when the tracking is stoping, the latest latlng is null.")
        }
    }

    fun onMove(from:Location?, to:Location){

        val cdistance = from?.distanceTo(to) ?: 0.0f
        var dtime =-1L

        if(from!=null){
            dtime = to.time - from.time
        }


        distance += cdistance

        whenPersistenceServiceReaderRun {
            whenExistsCurrentUserAndTrackingStartRun {

                MileageTrackerApplication.getCurrentTracking()?.apply {
                    this.miles = Math.ceil(distance.toDouble()).toLong()
                }

                TaskUtils.runInBackground{
                    val loc = LocationEntity().apply {
                        this.longitude = to.longitude
                        this.latitude = to.latitude
                        this.userId = MileageTrackerApplication.getCurrentAccount()?.id
                        this.trackingId = MileageTrackerApplication.getCurrentTracking()?.id
                    }

                    if(dtime>0){

                        val speed = cdistance.toFloat()/(dtime/1000)
                        when(speed){
                            in 0 .. 30 -> loc.locType = 0
                            in 31 .. 50 -> loc.locType = 1
                            else -> loc.locType = 2
                        }

                        Log.v("console", "speed:$speed m/s, and type is ${loc.locType}")
                    }

                    PersistenceService.service?.saveLocations(loc)

                    TaskUtils.runInUiThread {
                        LOCATION_ENTITY_CREATE_DELEGATES.forEach{
                            it(loc)
                        }
                    }
                }
                TaskUtils.runInBackground {
                    whenNotTrackingFromAddressRun {
                        AddressUtils.recoAddress(to)?.also {
                            MileageTrackerApplication.getCurrentTracking()?.apply {
                                startLatitude = to.latitude
                                startLongitude = to.longitude
                                startAddress = it
                                PersistenceService.service?.saveTracking(this)
                            }
                        }
                    }
                }

            }
        }
    }

    //当位置改变的时候调用
    @Override
    override fun onLocationChanged(location: Location) {
        Log.v("console", "location change:${location.latitude} ${location.longitude}")

        isTrackingStopedRun {
            Log.v("console", "stop location listening!!!")
            locationManager.removeUpdates(this)
            LocationService.CURRENT_LOCATION_SERVICE?.stop()
        }
        LocationService.LOCATION_UPDATE_DELEGATES.forEach{delegate->
            delegate(latestLocation, location)
        }
        latestLocation = location
    }

    //当GPS状态发生改变的时候调用
    @SuppressLint("MissingPermission")
    override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {
        when (status) {
            LocationProvider.AVAILABLE -> {
                androidLocationPermissionOpenAnyDecideRun {
                    Log.w("console", "Location service status is available.")
                    LocationService.CURRENT_LOCATION_SERVICE?.serviceImpl?.stopTracking()
                }
            }
            LocationProvider.OUT_OF_SERVICE, LocationProvider.TEMPORARILY_UNAVAILABLE -> {
                Log.w("console", "Location service status is out of service or temporarily unavailable, it would not save locations.")
            }
        }
    }

    @SuppressLint("MissingPermission")
    override fun onProviderEnabled(provider: String) {
        Log.i("console", "Location service provider $provider is enabled.")
        androidLocationPermissionOpenAnyDecideRun {
            //locationManager.requestLocationUpdates(provider, LOCATION_MIN_TIME, LOCATION_MIN_DISTANCE, this)
        }
    }

    override fun onProviderDisabled(provider: String) {
        Log.w("console", "Location service provider $provider is disabled.")
    }
}


/**
 * .
 */
class LocationService : Service() {

    lateinit var serviceImpl: LocationServiceImpl

    private lateinit var locationManager: LocationManager

    companion object {
        var CURRENT_LOCATION_SERVICE: LocationService? = null

        var LOCATION_UPDATE_DELEGATES = mutableListOf<(Location?, Location)->Unit>()

        var LOCATION_ENTITY_CREATE_DELEGATES= mutableListOf<(LocationEntity)->Unit>()
    }

    override fun onBind(intent: Intent): IBinder? {
        return serviceImpl
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        startForeground(9999, buildNotification())
        Log.v("console", "service start")
        serviceImpl.stopTracking()
        serviceImpl.initTracking()
        beginListenLocations()
        return START_STICKY
    }

    override fun onCreate() {
        Log.v("console", "service create")
        CURRENT_LOCATION_SERVICE = this
        PersistenceService.bind(application)
        serviceImpl = LocationServiceImpl()
        locationManager = MileageTrackerApplication.getCurrentApplication().getSystemService(Context.LOCATION_SERVICE) as LocationManager
        serviceImpl.locationManager = locationManager
        super.onCreate()
    }


    @SuppressLint("MissingPermission")
    fun beginListenLocations() {
        val criteria = Criteria()
        criteria.accuracy = Criteria.ACCURACY_FINE
        criteria.isAltitudeRequired = false
        criteria.isBearingRequired = false
        criteria.isCostAllowed = true
        criteria.powerRequirement = Criteria.POWER_HIGH

        var provider = locationManager.getBestProvider(criteria, true)

        val providerList = locationManager.getProviders(true)
        when {
            providerList.contains(LocationManager.GPS_PROVIDER) -> provider = LocationManager.GPS_PROVIDER
            providerList.contains(LocationManager.NETWORK_PROVIDER) -> provider = LocationManager.NETWORK_PROVIDER
            else -> ToastUtils.showToastLong("Please Open Your GPS or Location Service")
        }

        if(StringUtils.isBlank(provider)&&!providerList.isEmpty()){
            provider = providerList[0]
        }

        if(StringUtils.isNotBlank(provider)) {
            if (androidLocationPermissionOpenAnyDecide.decide()) {
                Log.w("console", "start requestLocationUpdates-----$provider")
                locationManager.requestLocationUpdates(provider, LOCATION_MIN_TIME, LOCATION_MIN_DISTANCE, serviceImpl)
            }
        }else{
            ToastUtils.showToastLong("Please Open Your GPS or Location Service ")
            providerList.forEach{
                Log.v("console", "provider:$it")
            }
        }

    }

    @SuppressLint("NewApi")
    fun buildNotification(): Notification {
        val channelId = "MillageTrackerLocationService"
        val channelName = "Milage Tracker's Location Service"

        val manager: NotificationManager = this.application.applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val builder: Notification.Builder

        builder = if (androidVersionAfter8V26Decide.decide()) {
            val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH)
            manager.createNotificationChannel(channel)
            Notification.Builder(this.applicationContext, channelId)
        } else {
            Notification.Builder(this)
        }

        androidVersionAfter4d4wV20DecideRun {
            builder.setLocalOnly(true)
        }

        androidVersionAfter4d2V17DecideRun {
            builder.setShowWhen(false)
        }

        builder.setContentTitle(channelId).setContentText(channelName)
                .setSmallIcon(R.mipmap.ic_launcher).setAutoCancel(false)

        return builder.build()
    }

    override fun onDestroy() {
        super.onDestroy()
    }

    fun stop() {
        Log.v("console", "Location Service is stoping")
        serviceImpl.stopTracking()
        stopSelf()
        MileageTrackerApplication.updateCurrentTracking(null)
    }

}
